#include "samplers.fx"

struct VS_IN
{
	float3	position	:	POSITION;
	float3	normal		:	NORMAL;
	float3	tangent		:	TANGENT;
	float2	tex			:	TEX_COORDS;
};

struct VS_OUT
{
	float3	position	:	LPOS;
	float3	normal		:	LNORMAl;
};

struct FireParticle
{
	float4 position		:	POSITION;
	float4 direction	:	DIRECTION; 
	float4	params		:	PARAMETERS;	// emitter : .X: ID, .Y: active: Z: age .W: unique ID (0,1,2,...)
										// particle: .X: age .Y: alpha   Z:     .W: type
};


Texture2D	texNoise;
uint		iObjectId;
float		fMinTriangleArea;
float		fMaxTriangleArea;
uint		iMinSamples;
uint		iMaxSamples;


float	triangleArea(float3 A, float3 B, float3 C)
{
	float res;

	res = A.x*(B.y-C.y) + B.x*(C.y-A.y) + C.x*(A.y-B.y);
	res *= 0.5f;
	res = abs(res);

	return res;
}

float3  triangleDistribution(float2 uv, float3 p0, float3 p1, float3 p2, float3 n0, float3 n1, float3 n2,
																						out float3 normal)
{
	uv *= 2.0f;

	float s = 1.0f - sqrt(1.0f - uv.x);
	float t = (1.0f - s)*uv.y;
	float3 res = p0 + s*(p1-p0) + t*(p2-p0);
	normal = n0 + s*(n1-n0) + t*(n2-n0);

	return res;
}



VS_OUT	VS(VS_IN	vin)
{
	VS_OUT vout;

	vout.position = vin.position;
	vout.normal = vin.normal;
	return vout;
}

[maxvertexcount(50)]
void GS(triangle VS_OUT gin[3], inout PointStream<FireParticle> gstream)
{

	float area = triangleArea(gin[0].position, gin[1].position, gin[2].position);

	float perc = (area-fMinTriangleArea) / (fMaxTriangleArea - fMinTriangleArea);
	if (perc > 0.0f) // area > min area
	{
		uint samples = iMinSamples + perc * (uint)(iMaxSamples-iMinSamples);

		for (uint i=0; i<samples; i++)
		{
			// pick random uv
			float index = i * (gin[0].position.x+gin[0].position.y);
			float3 rnd = texNoise.SampleLevel(s_linear, float2(index, -index*0.5f), 0).rgb;
		
			// transform to point coords on the triangle
			float3 pos, normal;
			pos = triangleDistribution(rnd.xy, gin[0].position, gin[1].position, gin[2].position,
										gin[0].normal, gin[1].normal, gin[2].normal, normal);
		
			// output
			FireParticle gout;
			gout.position = float4(pos, 1.0f);
			gout.direction = float4(normal, 0.0f);
			gout.params = float4(/*objID*/iObjectId, /*active*/0, /*age*/0, /*emitterID*/0); // emitterID will
						  // be update by a later pass

			gstream.Append(gout);
		}
	}
}



GeometryShader pGS = CompileShader(gs_4_0, GS());

GeometryShader gsStreamOut = 
			ConstructGSWithSO( 
								pGS, 
								"POSITION.xyzw; DIRECTION.xyzw; PARAMETERS.xyzw"
							);




DepthStencilState disabled
{
	DepthEnable = false;
	DepthWriteMask = false;
};


technique10 ProceduralEmitters
{
	pass P0
	{
		SetVertexShader( CompileShader ( vs_4_0, VS() ) );
		SetGeometryShader( gsStreamOut );
		SetPixelShader ( NULL);

		SetDepthStencilState(disabled, 0);
	}
}


///////////////////////////////////////////////////////////////


FireParticle VS_ID(FireParticle vin)
{
	
	return vin;
}

[maxvertexcount(1)]
void GS_ID(point FireParticle gin[1], inout PointStream<FireParticle> gstream, uint primID : SV_PrimitiveID)
{
	FireParticle p = gin[0];
	p.params.w = (float)primID;
	gstream.Append(p);
}



GeometryShader pGS_ID = CompileShader(gs_4_0, GS_ID());

GeometryShader gsStreamOutID = 
			ConstructGSWithSO( 
								pGS_ID, 
								"POSITION.xyzw; DIRECTION.xyzw; PARAMETERS.xyzw"
							);




technique10 EmmitersID
{
	pass P0
	{
		SetVertexShader( CompileShader ( vs_4_0, VS_ID() ) );
		SetGeometryShader( gsStreamOutID );
		SetPixelShader ( NULL);

		SetDepthStencilState(disabled, 0);
	}
}